package es

import (
	"context"
	"time"

	"gitee.com/mkwchecking/sr-core/log"
	"gitee.com/mkwchecking/sr-core/yal/math"

	es "github.com/olivere/elastic/v7"
)

func NewClient(cfg *Config) (IEsClient, error) {

	esCli, err := es.NewClient(
		es.SetURL(cfg.URL),
		es.SetBasicAuth(cfg.Username, cfg.Password),
		es.SetSniff(cfg.Sniff),
		es.SetHealthcheck(cfg.Healthcheck),
		es.SetHealthcheckInterval(10*time.Second),
		es.SetGzip(true),
		es.SetErrorLog(newEsLogger(log.ErrorLevel)),
		es.SetInfoLog(newEsLogger(log.InfoLevel)),
	)
	if err != nil {
		return nil, err
	}

	return &Client{
		client: esCli,
	}, nil
}

type Client struct {
	client *es.Client
}

func (c *Client) Search(index string) IEsSearcher {
	return &searcher{
		svc: c.client.Search(index),
	}
}

type searcher struct {
	svc *es.SearchService
}

func (s *searcher) Searcher() *es.SearchService {
	return s.svc
}

func (s *searcher) Sort(field string, asc bool, unmaptag ...string) IEsSearcher {
	f := es.NewFieldSort(field)
	if asc {
		f.Asc()
	} else {
		f.Desc()
	}
	if len(unmaptag) > 0 {
		f.UnmappedType(unmaptag[0])
	}
	s.svc.SortBy(f)
	return s
}

func (s *searcher) Do(ctx ...context.Context) *ESResult {
	var result *es.SearchResult
	var err error

	if len(ctx) > 0 {
		result, err = s.svc.Do(ctx[0])
	} else {
		result, err = s.svc.Do(context.Background())
	}
	if err != nil {
		return &ESResult{
			err: err,
		}
	}

	return &ESResult{
		result: result,
	}
}

func (s *searcher) Page(pageNum, pageSize int) IEsSearcher {
	page := math.Max(pageNum, 1)
	size := math.Min(pageSize, 10)
	offset := (page - 1) * size

	s.svc.From(offset).Size(size)
	return s
}

func (s *searcher) Query(q es.Query) IEsSearcher {
	s.svc.Query(q)
	return s
}

func (s *searcher) WhereAnd(q func(*es.BoolQuery)) IEsSearcher {
	bq := es.NewBoolQuery()
	q(bq)
	s.svc.Query(bq)
	return s
}

func (s *searcher) Scan(data any) error {
	res := s.Do()
	if res.Error() != nil {
		return res.Error()
	}

	return res.Scan(data)
}
